
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>mybatis分页原理 - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>mybatis分页原理 - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">MyBatis 分页插件的底层实现综合运用了哪些核心技术和设计模式？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">MyBatis 分页插件的底层实现是基于拦截器机制，它综合运用了责任链模式、动态代理和反射机制。</div>
          </div>
          <div class="card-source">来源: 文档开篇第一段</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">请简述 MyBatis 分页插件的四个核心工作步骤。</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">分页插件的工作流程包括四个核心步骤：1. 拦截 SQL 语句：通过实现 Interceptor 接口拦截 Executor 的 query 方法。2. 修改 SQL 语句：根据分页参数动态添加如 LIMIT、ROWNUM 等分页条件。3. 执行分页查询：将修改后的 SQL 传递给 Executor 执行。4. 封装结果：将查询结果和分页信息（如总数、页码）封装到 Page 对象中。</div>
          </div>
          <div class="card-source">来源: 文档中「2. 分页插件的工作流程」部分</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">MyBatis 分页插件如何实现对多种数据库（如 MySQL 和 Oracle）的分页语法兼容？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">插件通过判断数据库类型来动态生成不同的分页 SQL。例如，对于 MySQL，它会添加 `LIMIT {offset}, {limit}` 子句；而对于 Oracle，它会使用 `ROWNUM` 或 `ROW_NUMBER()` 函数来实现分页。</div>
          </div>
          <div class="card-source">来源: 文档中「2. 分页插件的工作流程」部分的“修改 SQL 语句”</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">根据文档，MyBatis 分页插件主要具备哪三大特性？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">分页插件的三大主要特性是：1. 动态 SQL 修改：根据参数自动生成特定数据库的分页SQL。2. 支持多种数据库：通过选择合适的分页语法来兼容多种数据库。3. 封装分页结果：将查询结果和分页信息封装成对象，方便处理和展示。</div>
          </div>
          <div class="card-source">来源: 文档中「4. 分页插件的主要特性」部分</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">在MyBatis拦截器机制中，`Plugin.wrap(target, this)` 方法的主要作用是什么？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">`Plugin.wrap` 方法用于创建代理对象。它会判断目标对象 `target` 是否实现了当前拦截器 `this` 注解中声明要拦截的接口。如果实现了，就为目标对象创建一个动态代理并返回；否则，直接返回目标对象本身。</div>
          </div>
          <div class="card-source">来源: 文档中「6.Plugin类」部分</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">在自定义 MyBatis 拦截器时，`invocation.proceed()` 方法的用途是什么？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">在拦截器的 `intercept` 方法中，调用 `invocation.proceed()` 会执行原始被拦截的方法。这使得我们可以在原始方法执行前后添加自定义逻辑，从而形成一个完整的调用链。</div>
          </div>
          <div class="card-source">来源: 文档中「6.Plugin类」部分</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">MyBatis 插件是通过拦截哪个核心组件的什么方法来捕获 SQL 查询过程的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">分页插件实现了 MyBatis 的 `Interceptor` 接口，并通过拦截 `Executor` 组件的 `query` 方法来捕获并处理 SQL 查询过程。</div>
          </div>
          <div class="card-source">来源: 文档中「2. 分页插件的工作流程」部分的“拦截 SQL 语句”</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">当分页查询完成后，插件通常会将结果封装成什么形式？除了数据列表，还包含哪些信息？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">插件通常会将查询结果封装到 `Page` 对象中。该对象除了包含实际的数据列表外，还包含了分页信息，例如当前页码、每页记录数和总记录数等。</div>
          </div>
          <div class="card-source">来源: 文档中「2. 分页插件的工作流程」部分的“封装结果”</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">在 `Plugin` 类的 `invoke` 方法中，是如何决定执行拦截逻辑还是直接调用原始方法的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">`Plugin` 类的 `invoke` 方法会判断当前执行的方法是否为预先定义好需要拦截的方法。如果是，它会将目标对象、方法和参数封装成一个 `Invocation` 对象，并调用拦截器的 `intercept` 方法；如果不是，则直接调用原始方法。</div>
          </div>
          <div class="card-source">来源: 文档中「6.Plugin类」部分</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
